/**
 * The user interface for WebGrADS
 **/


// Constructor
// --------------------------------------------------------------------------

function GaWebUI(gaweb) {
    this.gaweb = gaweb;

    // Whether or not webgrads is set to 'hold' plots. Works similar to
    // "hold on"/"hold off" in matlab. Defaults to off.
    this.holding = false;

    // The last expression displayed and it's title. Both Default to null.
    this.last_display_expr = null;
    this.last_title_expr = null;
}


// --------------------------------------------------------------------------
// Utility Functions
// --------------------------------------------------------------------------

// Converts a menu object to a DOM element.
//
// Params:
//   a (k, v) pair at the level of processing
// Returns:
//   an element to be appendChild'd to the parent
function menuToElement(e) {
    var ks = [];
    var vs = [];
    for (var k in e) {
	ks.push(k);
	vs.push(e[k]);
    }

    var n = ks.length;
    if (n == 1) {
	var k = ks[0];
	var v = vs[0];

	if (typeof(v) == "string") {
	    var a = $('<a>').attr({'href': 'javascript:' + v}).append(k);
	    a.addClass('accessible');
	    var s = $('<span>');
	    s.append(a);
	    return s[0];
	}
	else { // assume array
	    var ul;
	    if (k == "menu") {
		ul = $('<ul class="jd_menu">');
	    } else {
		ul = $('<ul>');
	    }

	    for (var i=0; i < v.length; i++) {
		var e = menuToElement(v[i]);
		if (e.trick) {
		    ul.append($('<li>').append(e.title).append(e.ul));
		} else {
		    ul.append($('<li>').append(e));
		}
	    }

	    if (k == "menu") {
		return ul[0];
	    } else {	
		var a = $('<a href="#">').append(k);
		a.addClass('accessible');
		var s = $('<span>').append(a).append(ul);
		return {'trick': true, 'title': k, 'ul': ul}
	    }
	}
    }
}

// Redoes the last display after the given command. If there have been no displays,
// does only the given command. 
//
// Params:
//   command: Command to prefix to sequence that redoes last display. For none, pass
//     the empty string.
GaWebUI.prototype.redoLastDisplayAfter = function(command) 
{
    if (this.last_display_expr) {
	command += '\n';

	if (! this.holding) {
	    command += 'clear\n';
	}

	command += 'd ' + this.last_display_expr + '\n';
	
	if (this.last_title_expr) {
	    command += 'draw title ' + this.last_title_expr + '\n';
	}

	command += 'cbarn\n';
    }

    this.dispatchCommand(command);
}

// Dispatch a command to the GrADS terminal to be processed immediately.
// The result of this function is the same as if the user had entered the command.
//
// Params:
//   command: command to dispatch
GaWebUI.prototype.dispatchCommand = function(command) {
    this.term.echo('ga-> ' + command);
    this.gaweb.handleTerminal(command, this.term);
}

// Displays a variable in a file. If hold is set to off, then this function
// clears the plot first. This always attempts to show a colorbar. This function
// makes use of the instance variables holding and sets
// this.last_display_expr, and this.last_title_expr.
//
// Params:
//   var_name: Name of the variable to display
//   file_num: Number of the file the variable is in
GaWebUI.prototype.displayVariable = function(var_name, file_num, var_descrip) {
    this.last_display_expr =  var_name + '.' + file_num;
    this.last_title_expr = var_name + ' - ' + var_descrip;
    
    var command = '';
    if (! this.holding) {
	command += 'clear\n';
    }
    command += 'd ' + this.last_display_expr + '\n';
    command += 'draw title ' + this.last_title_expr + '\n';
    command += 'cbarn\n';

    this.dispatchCommand(command);
}
    

// --------------------------------------------------------------------------
// Refresh Functions
// --------------------------------------------------------------------------

// Refresh the GrADS Window image.
//
// Params:
//   imageSrc: new image source for the GrADS Window image.
GaWebUI.prototype.refreshGrADSWindowImage = function(imageSrc) {
    // If I make a new <img> each time, a blink occurs. Let's
    // only do this once.
    if ($('#grads-window-img').length == 0) {
	var img = $('<img>');
	img.attr('id', 'grads-window-img');
	$('#grads-window-image').append(img);
    }
    
    $('#grads-window-img').attr('src', imageSrc);
}

// Refresh a navigator dialog that already exists as #navigator-dialog.
// Calls to this with no navigator dialog open will be a no-op.
//
// Params:
//   dims: Dimension information from the backend. Can be null (this has a meaning).
//   ctlinfo: Dimension information from the backend. Can be null (this has a meaning).
GaWebUI.prototype.refreshNavigatorDialog = function(dims, ctlinfo) {
    var me = this;

    // Gets the GrADS time from the form
    var getGrADSTime = function() {
	var tabIndex = $('#select-t-tabs').tabs('option', 'selected');
	
	if (tabIndex == 0) {	
	    var hour = $('#fixed-time-hour-select').val();
	    var min = $('#fixed-time-min-select').val();
	    var rest = $('#fixed-time-date-picker').val();
	    return hour + ':' + min + 'Z' + rest;
	} else {
	    var hour, min, rest;
	    
	    hour = $('#varying-time-hour-select1').val();
	    min = $('#varying-time-min-select1').val();
	    rest = $('#varying-time-date-picker1').val();
	    var startTime = hour + ':' + min + 'Z' + rest;

	    hour = $('#varying-time-hour-select2').val();
	    min = $('#varying-time-min-select2').val();
	    rest = $('#varying-time-date-picker2').val();
	    var endTime = hour + ':' + min + 'Z' + rest;
	    
	    if (startTime.toLowerCase() == endTime.toLowerCase()) {
		return startTime;
	    } else {
		return startTime + ' ' + endTime;
	    }
	}
    }

    // The refresh method for if there are no dims available
    // ---------------------------------------
    var refreshAllNoDims = function() {
	$('#z-plus-button').button('disable');
	$('#z-minus-button').button('disable');	
	$('#t-plus-button').button('disable');
	$('#t-minus-button').button('disable');

	$('#navigator-date-label').html('');
	$('#navigator-lev-label').html('');
	
	$('#select-z-link').hide();
	$('#select-t-link').hide();
	$('#select-spatial-link').hide()

	$('#select-z').hide();
	$('#select-t').hide();
    }

    // Refreshes the main navigation at the top
    // ---------------------------------------
    var refreshMainNav = function() {
	// Reset everything
	$('#z-minus-button').button('enable');	
	$('#z-plus-button').button('enable');
	$('#t-minus-button').button('enable');
	$('#t-plus-button').button('enable');

	$('#z-minus-button').unbind('click');
	$('#z-plus-button').unbind('click');
	$('#t-minus-button').unbind('click');
	$('#t-plus-button').unbind('click');

	// Check if buttons are out of range, and disable if so
	var z = dims.z[0];
	if (z <= 1) {
	    $('#z-minus-button').button('disable');
	}
	if (z >= ctlinfo.nz) {
	    $('#z-plus-button').button('disable');
	}

	var t = dims.t[0];
	if (t <= 1) {
	    $('#t-minus-button').button('disable');
	}
	if (t >= ctlinfo.nt) {
	    $('#t-plus-button').button('disable');
	}
	
	// Attach button click handlers
	$('#z-plus-button').click(function() {
		me.redoLastDisplayAfter('set z ' + (dims.z[0] + 1));
	    });
	$('#z-minus-button').click(function() {
		me.redoLastDisplayAfter('set z ' + (dims.z[0] - 1));
	    });
	$('#t-plus-button').click(function() {
		me.redoLastDisplayAfter('set t ' + (dims.t[0] + 1));
	    });
	$('#t-minus-button').click(function() {
		me.redoLastDisplayAfter('set t ' + (dims.t[0] - 1));
	    });

	// Refresh labels
	$('#navigator-lev-label').html(dims.lev[0]);

	if (dims.t[0] == dims.t[1]) {
	    $('#navigator-date-label').html(dims.time[0] + ' (' + dims.t[0] + '/' + ctlinfo.nt + ')');
	} else {
	    $('#navigator-date-label').html('Start: ' + dims.time[0] + ' (' + dims.t[0] + '/' + ctlinfo.nt + ')'
					    + '<br>End: ' + dims.time[1] + ' (' + dims.t[1] + '/' + ctlinfo.nt + ')');
	}
    }

    /// Refreshez the Z link and contents
    var refreshZSection = function() {	
	// Display the Z link only if they have something to select (nz > 1)
	if (ctlinfo.nz > 1) {
	    $('#select-z-link').show();
	} else {
	    $('#select-z-link').hide();
	}       

	// Create dropdown menu for levels
	if (ctlinfo.nz == 1) {
	    $('#level-container').hide();
	} else {
	    var select = $('<select>');  
	    select.attr('id', 'level-select');
	    for (var i=ctlinfo.nz; i >= 1; i--) {
		var opt = $('<option>').val(i).html(ctlinfo.zlevs[i-1]);		
		if (i == dims.z[0]) {
		    opt.attr('selected', 'selected');
		}
		select.append(opt);
	    }

	    $('#level-picker-container').empty();
	    $('#level-picker-container').append(select);
	    $('#level-container').show();

	    // Attach click handlers for levels drop down
	    $('#level-select').change(function(event) {
		    var lev = $('#level-select').val();
		    me.redoLastDisplayAfter('set z ' + lev);
		});
	}

    }
    
    // Refreshes the T link and contents
    // -----------------------------------
    var refreshTSection = function() {
	// Display the T link only if they have something to select (nt > 1)
	if (ctlinfo.nt > 1) {	   
	    $('#select-t-link').show();
	} else {
	    $('#select-t-link').hide();
	}

	// Refresh labels
	$('#navigator-start-time').html(ctlinfo.t0);
	$('#navigator-num-tsteps').html(ctlinfo.nt);

	// Make hour pickers in fixed and varying tabs
	var makeHourPicker = function(id, defaultHour) {
	    var select = $('<select>');		
	    select.attr('id', id);
	    for (var i=0; i < 24; i += 1) {
		var s = i.toString();
		if (s.length == 1) {
		    s = '0'+s;
		}
		var opt = $('<option>').val(s).html(s);
		if (s == defaultHour) {
		    opt.attr('selected', 'selected');
		}	    
		select.append(opt);
	    }
	    if (id == 'fixed-time-hour-select') {		
		select.change(function(event) { me.redoLastDisplayAfter('set time ' + getGrADSTime()); });
	    }
	    return select;
	}	

	var a = dims.time[0].split('Z')[0].split(':');
	var b = dims.time[1].split('Z')[0].split(':');

	$('#fixed-time-hour-picker').empty();
	$('#fixed-time-hour-picker').append(makeHourPicker('fixed-time-hour-select', a[0]));

	$('#varying-time-hour-picker1').empty();
	$('#varying-time-hour-picker1').append(makeHourPicker('varying-time-hour-select1', a[0]));
						
	$('#varying-time-hour-picker2').empty();
	$('#varying-time-hour-picker2').append(makeHourPicker('varying-time-hour-select2', b[0]));

	// Set options of datepickers
	var minDate = ctlinfo.t0.split('Z')[1];
	var maxDate = ctlinfo._tf.split('Z')[1];

	var yearRange = minDate.substring(minDate.length - 4) + ':' + maxDate.substring(maxDate.length - 4);

	$('#fixed-time-date-picker').unbind('onSelect');
	$('#fixed-time-date-picker').datepicker('option', 'minDate', minDate);
	$('#fixed-time-date-picker').datepicker('option', 'defaultDate', dims.time[0].split('Z')[1]);
	$('#fixed-time-date-picker').datepicker('option', 'maxDate', maxDate);
	$('#fixed-time-date-picker').datepicker('option', 'yearRange', yearRange);
	$('#fixed-time-date-picker').val(dims.time[0].split('Z')[1]);
	$('#fixed-time-date-picker').bind({
		onSelect: function(event) {
		    me.redoLastDisplayAfter('set time ' + getGrADSTime());
		}});

	$('#varying-time-date-picker1').datepicker('option', 'minDate', minDate);
	$('#varying-time-date-picker1').datepicker('option', 'defaultDate', dims.time[0].split('Z')[1]);
	$('#varying-time-date-picker1').datepicker('option', 'maxDate', maxDate);
	$('#varying-time-date-picker1').datepicker('option', 'yearRange', yearRange);
	$('#varying-time-date-picker1').val(dims.time[0].split('Z')[1]);

	$('#varying-time-date-picker2').datepicker('option', 'minDate', minDate);
	$('#varying-time-date-picker2').datepicker('option', 'defaultDate', dims.time[1].split('Z')[1]);
	$('#varying-time-date-picker2').datepicker('option', 'maxDate', maxDate);
	$('#varying-time-date-picker2').datepicker('option', 'yearRange', yearRange);
	$('#varying-time-date-picker2').val(dims.time[1].split('Z')[1]);
		
	// Make the minute pickers
	var makeMinPicker = function(id, defaultMin) {
	    var select = $('<select>');
	    select.attr('id', id);
	    for (var i=0; i < 60; i += 30) {
		var s = i.toString();
		if (s.length == 1) {
		    s = '0'+s;
		}
		var opt = $('<option>').val(s).html(s);

		if (s == defaultMin) {
		    opt.attr('selected', 'selected');
		}
		select.append(opt);
	    }	

	    if (id == 'fixed-time-min-select') {
		select.change(function(event) { me.redoLastDisplayAfter('set time ' + getGrADSTime()); });
	    }

	    return select;
	}
	
	$('#fixed-time-min-picker').empty();
	$('#fixed-time-min-picker').append(makeMinPicker('fixed-time-min-select', a[1]));

	$('#varying-time-min-picker1').empty();
	$('#varying-time-min-picker1').append(makeMinPicker('varying-time-min-select1', a[1]));

	$('#varying-time-min-picker2').empty();
	$('#varying-time-min-picker2').append(makeMinPicker('varying-time-min-select2', b[1]));

	// Attach to apply button
	$('#select-t-apply').unbind('click');
	$('#select-t-apply').click(function() {
		me.redoLastDisplayAfter('set time ' + getGrADSTime());
	    });	

	// Hide animation tab if an opendap dataset that is not the monsoon dataset.
	if (ctlinfo.dset.toLowerCase().indexOf('http://') == 0
	    && ctlinfo.dset != 'http://monsoondata.org:9090/dods/model') {
	    $('#varying-time-enabled').hide();
	    $('#varying-time-disabled').show();
	} else {
	    $('#varying-time-enabled').show();
	    $('#varying-time-disabled').hide();
	}
    }
    
    if (dims == null) {
	refreshAllNoDims();
    } else {
	refreshMainNav();
	refreshZSection();
	refreshTSection();
	$('#select-spatial-link').show();
    }
}

// Refreshes an display dialog that already exists as #display-dialog.
// Calls to this with no display dialog open will be a no-op.
//
// Params
//   data: Data returned by a call to the display template. Should include
//     'template' and 'files' properties.
GaWebUI.prototype.refreshDisplayDialog = function(data) {
    if ($('#display-dialog').length == 0) {
	return;
    }
    
    var me = this;

    var container = $('#display-dialog');
    container.empty()
    container.html(data.template);
     
    // Create buttons on the dialog
    $("button").button();
    $('#hold-button-set').buttonset();
    
    // Attach callbacks to buttons on the page
    $('#clear-plot-button').click(function() { me.dispatchCommand('clear'); });    

    $('#hold-on-button').click(function() { me.holding = true; });    
    $('#hold-off-button').click(function() { me.holding = false; });
    
    me.holding = false; // start with holding off, as displayed in the termplate
    
    $('#display-expr-button').click(function() {
	    var input = $('<input>');
	    input.attr('type', 'text');
	    input.attr('id', 'expr-input');

	    if (me.last_display_expr !== null) {
		input.attr('value', me.last_display_expr);
	    }

	    var setButton = $('<button>');
	    setButton.attr('id', 'set-button');
	    setButton.css('margin-left', '10px');
	    setButton.append('Set');
	    setButton.button();

	    var container = $('<div>');
	    container.attr('id', 'expr-prompt');
	    container.append(input);
	    container.append(setButton);

	    me.term.disable();  // prevents term from capturing input

	    container.dialog({title: 'Set Display Expression',
			height: 80,
			close: function() { me.term.enable(); $('#expr-prompt').remove(); }
		});


	    input.keypress(function(event) {
		    if (event.which == 13) {
			$('#set-button').click();
		    }
		});

	    setButton.click(function() {
		    var val = $('#expr-input').val().trim();

		    if (val == '') {
			alert('You did not enter an expression!');
		    } else {
			container.dialog('close');
			me.term.enable();

			me.last_display_expr = val;
			me.last_title_expr = val;

			me.redoLastDisplayAfter('');
		    }
		});	    
	});

    // We need to attach one click handler to each variable in every file.
    // the files struct is generously passed from the backend, so we traverse
    // this to span the same set of button id's as the code in the template.
    var files = data.files;
    for (var i=0; i < files.length; i++) {
	if (files[i] == null) {
	    continue;
	}
	for (var j=0; j < files[i].length; j++) {
	    var varName = files[i][j][0];
	    var varDescrip = files[i][j][3];
	    var fileNum = i + 1;			  
	    var btnId = 'file'+fileNum+'-'+varName+'-button';
	    var btn = $('#'+btnId);
	    btn.click((function(v, n, d) {
			return function() {
			    me.displayVariable(v, n, d);
			};
		    })(varName, fileNum, varDescrip)); 
	}
    }
    
    // Attach click handler to the X, which only appears on the last file.
    var lastIndex = -1;
    for (var i=0; i < files.length; i++) {
	if (files[i] !== null) {
	    lastIndex = i;
	}
    }
    
    if (lastIndex >= 0) {
	$('#close-file-button').click(function() {
		me.dispatchCommand('close ' + (lastIndex + 1));
	    });
    }
}

// Refreshs the sliders with diminfo state.
//
// Params
//   dims: Dimension information from the backend. Can be null (this has a meaning).
//   ctlinfo: Dimension information from the backend. Can be null (this has a meaning).
GaWebUI.prototype.refreshSliders = function(dims, ctlinfo) {

    var me = this;

    if (dims == null || ctlinfo == null) {  // in reality, dims is null IFF ctlinfo is null
	$('#grads-window-tslider').slider('disable');
	$('#grads-window-zslider').slider('disable');
    } else {    
	// Enable sliders
	$('#grads-window-tslider').slider('enable');
	$('#grads-window-zslider').slider('enable');	

	// Unbind sliders
	$('#grads-window-tslider').unbind('slidechange');
	$('#grads-window-zslider').unbind('slidechange');
	
	// Setup time slider
	if (ctlinfo.nt == 1) {
	    $('#grads-window-tslider').slider('disable');
	} else {
	    $('#grads-window-tslider').slider('option', 'min', 1);
	    $('#grads-window-tslider').slider('option', 'max', ctlinfo.nt);
	    $('#grads-window-tslider').slider('option', 'step', 1);
	    $('#grads-window-tslider').slider('option', 'value', dims.t[0]);
	    $('#grads-window-tslider-time').empty();
	    $('#grads-window-tslider-time').append(dims.time[0]);

	    $('#grads-window-tslider').unbind('slidechange');
	    $('#grads-window-tslider').bind('slidechange', function(event, u) {
		    $('#grads-window-tslider-time').val(u.value);
		    me.redoLastDisplayAfter('set t ' + u.value);
		    });	    
	}
	
	// Setup level slider
	if (ctlinfo.nz == 1) {
	    $('#grads-window-zslider').slider('disable');
	} else {
	    $('#grads-window-zslider').slider('option', 'min', 1);
	    $('#grads-window-zslider').slider('option', 'max', ctlinfo.nz);
	    $('#grads-window-zslider').slider('option', 'step', 1);
	    $('#grads-window-zslider').slider('option', 'value', dims.z[0]);

	    $('#grads-window-zslider').bind('slidechange', function(event, u) {
		    me.redoLastDisplayAfter('set z ' + u.value);
		    });
	}
    }
}


// --------------------------------------------------------------------------
// Public Functions
// --------------------------------------------------------------------------

// Initialize the user interface, This is called immediately after an init
// ajax call is complete.
//
// Params:
//   menu: Menu object/structure sent from the backend. Might be null.
GaWebUI.prototype.init = function(menu) {
    var me = this;

    // Hide Loading page, show main page
    $('#loading-page').hide();
    $('#main-page').show();

    // Build the menus (set to be on top of windows)
    if (menu !== null) {
	var menuContainer;
	menuContainer = $('<div>');
	menuContainer.attr('id', 'menu');
	menuContainer.append(menuToElement(menu));
	$('#main-page').append(menuContainer);
	$('ul.jd_menu').jdMenu();
    }
    
    // Build the dialog container for the GrADS Window. 
    var gradsWindow = $('<div>');
    gradsWindow.attr('id', 'grads-window');
    gradsWindow.css('background-color', 'black');

    $('#main-page').append(gradsWindow);

    $('#grads-window').dialog({title: 'GrADS Window',
                               width: 640,
		               height: 475,
  		               resizable: false
		});   
    
    // Add contents to dialog
    var zslider = $('<div id="grads-window-zslider">');
    zslider.css({float: 'right',
		'margin-top': '40px'});   
    zslider.slider({orientation: 'vertical'});
    zslider.hide();
    gradsWindow.append(zslider);

    var loading = $('<img id="grads-window-loading">');
    loading.attr('src', '/static/images/loading_circle.gif');
    loading.css({right: '8px',
		position: 'absolute'});
    loading.hide();
    gradsWindow.append(loading);

    var windowImage = $('<div id="grads-window-image">');
    gradsWindow.append(windowImage);

    var tsliderTime = $('<div id="grads-window-tslider-time">');
    tsliderTime.css('float', 'right');
    tsliderTime.hide();
    gradsWindow.append(tsliderTime);
    gradsWindow.append($('<br>'));

    var tslider = $('<div id="grads-window-tslider">');
    tslider.slider();
    tslider.hide();
    gradsWindow.append(tslider);

    gradsWindow.hoverIntent({
	    over: function(event) {
		if ($('#grads-window-img').length > 0) {
		    zslider.fadeIn('slow');
		    tsliderTime.fadeIn('slow');
		    tslider.fadeIn('slow');
		}
	    },
	    out: function(event) {
		zslider.hide();
		tsliderTime.hide();
		tslider.hide();		
	    }
	    });
    
    $(".ui-dialog-titlebar-close").hide();  // hack to hide X on GrADS Window

    // Build the grads terminal. Keep GrADS Terminal container alive even when the dialog
    // is closed by hiding it in the main page. We must also disable the term when another
    // input widget has focus; otherwise the terminal will steal focus.
    var gradsTermContainer = $('<div>');
    gradsTermContainer.attr('id', 'grads-terminal');
    gradsTermContainer.css('overflow-x', 'hidden');

    var container = $('<div>');
    container.attr('id', 'grads-terminal-container');
    container.append(gradsTermContainer);
    $('#main-page').append(container);
    container.hide();
    
    me.term = gradsTermContainer.terminal(
	function(command, term){me.gaweb.handleTerminal(command, term);},
	{greetings: 'Grid Analysis and Display System (GrADS)',
         name: 'grads-terminal',
         prompt: 'ga->',
	 clear: false,
	 height: '100%',
	 width: '100%'
	});
    
    // Build and display the Exit button (position it to be at the in the top right).
    // Unfortunately, our exit button cannot close the window, because the policy
    // in modern browsers is that you can only use javascript to close windows that
    // you opened in javascript.
    var exitButton = $('<div>');    
    exitButton.attr('id', 'exit-button');
    exitButton.append('Exit');
    $('#main-page').append(exitButton);

    exitButton.click(function() {
	    var d = $('<div>');
	    d.append('To exit WebGrADS, simply close the browser tab.<br><br>Thanks for using WebGrADS!');
	    d.dialog({modal: true, title: 'Exit'});
	});

    // Handle the resize of the window by repositioning everything
    $(window).resize(function() {
	    me.resizeNavigator();
	    me.resizeDisplayVariables();
	    me.resizeGrADSWindow();
	});

    // Finally, show the open dialow
    me.showOpenDialog();
}

// Resizing callback for GrADS Window. If the dialog is not open, does nothing.
GaWebUI.prototype.resizeGrADSWindow = function() {
    if ($('#grads-window').length > 0) {

	// The GrADS Window is not resizable, so we do not have to worry about
	// changing the size, just positioning.
	var smartX = $(window).width() / 2 - (1/2) * $('#grads-window').width();
	var smartY = 60;
	
	$('#grads-window').dialog('option', 'position', [smartX, smartY]);
    }
}

// Resizing callback for Navigator. If the dialog is not open, does nothing.
GaWebUI.prototype.resizeNavigator = function() {
    if ($('#navigator-dialog').length > 0) {
	var width = 230;
	var height = $(window).height() - 100;
	var smartX = 20;
	var smartY = 50;
	
	$('#navigator-dialog').dialog('option', 'position', [smartX, smartY]);
	$('#navigator-dialog').dialog('option', 'width', width);
	$('#navigator-dialog').dialog('option', 'height', height);
	$('#navigator-dialog').dialog('moveToTop');
    }
}

// Resizing callback for the Display Dialog. If the dialog is not open, does nothing.
GaWebUI.prototype.resizeDisplayVariables = function() {
    if ($('#display-dialog').length > 0) {
	var width = 225;
	var height = $(window).height() - 110;
	var smartX = $(window).width() - 280;
	var smartY = 70;		 
	
	$('#display-dialog').dialog('option', 'position', [smartX, smartY]);
	$('#display-dialog').dialog('option', 'width', width);
	$('#display-dialog').dialog('option', 'height', height);
	$('#display-dialog').dialog('moveToTop');
    }       
}

// Function to set the loading status visually
GaWebUI.prototype.setLoading = function(value) {
    if (value) {
	$('#grads-window-loading').fadeIn('fast');
    } else {
	$('#grads-window-loading').hide();
    }
}

// Menu callback from when "Set Contour" is selected
GaWebUI.prototype.setContourFromMenu = function() {
    this.redoLastDisplayAfter('set gxout contour');
}

// Menu callback from when "Set Shaded" is selected
GaWebUI.prototype.setShadedFromMenu = function() {
    this.redoLastDisplayAfter('set gxout shaded');
}

// A method to be called to fetch the "About" dialog and display it
// to the user.
GaWebUI.prototype.showAboutDialog = function() {

    if ($('#about-dialog').length > 0) {
	$('#about-dialog').dialog('moveToTop');
	return;
    }

    $.getJSON('/template',
              {'template_id': 'about'},
	      function(data) {
		  var container;
		  container = $('<div>');
		  container.attr('id', 'about-dialog');
		  container.html(data.template);
		  container.dialog({title: 'About',
			            close: function() {container.remove();}});
	      });
}

// A method to be called to fetch the "/nodestats" information and present it
// to the user. The exact output of /nodestats is what gets displayed in a 
// dialog
GaWebUI.prototype.showDebugInfoDialog = function() {
    if ($('#debuginfo-dialog').length > 0) {
	$('#debuginfo-dialog').dialog('moveToTop');
	return;
    }

    var me = this;
    $.getJSON('/ajax',
	      {'task': 'nodestats',
	       'session_id': this.gaweb.session_id},
	      function(data) {
		  var container;
		  container = $('<pre>');
		  container.attr('id', 'debuginfo-dialog');
		  container.append(data.text + '\nSID: ' + me.gaweb.session_id);
		  container.dialog({title: 'Debug Info',
			            close: function() {container.remove();}});

	      });		  
    
}

// Displays the "Display Variables" dialog to the user.
GaWebUI.prototype.showDisplayDialog = function() {
    var width = 225;
    var height = $(window).height() - 110;
    var smartX = $(window).width() - 280;
    var smartY = 70;		 

    var me = this;

    if ($('#display-dialog').length > 0) {
	me.resizeDisplayVariables();
	return;
    }

    $.getJSON('/template',
              {'template_id': 'display',
	       'session_id': this.gaweb.session_id},
	      function(data) {
		  var container;
		  container = $('<div>');
		  container.attr('id', 'display-dialog');

		  // Create display dialog 
		  var allNull = true;
		  for (var i=0; i < data.files.length; i++) {
		      if (data.files[i] != null) {
			  allNull = false;
			  break;
		      }
		  }

		  if ( allNull ) {
		      container.dialog({title: 'Display Variables',
				        position: [smartX, smartY],
				        close: function() {
				           container.empty();
					   container.remove();
			      }});
		      container.html(data.template);
		  } else {		  
		      container.dialog({title: 'Display Variables',
				  width: width,
				  height: height,
				  close: function() {
				    container.empty();
				    container.remove();
   			          },
				  position: [smartX, smartY]
				  });

		    // Call function to populate contents
		    me.refreshDisplayDialog(data);
		    me.showDisplayDialog(); // sizing for FF6
		  }});
}

// Displays the "Navigator" dialog to the user
GaWebUI.prototype.showNavigatorDialog = function() {
    var me = this;

    var width = 255;
    var height = $(window).height() - 100;
    var smartX = 20;
    var smartY = 50;
    
    if ($('#navigator-dialog').length > 0) {
	me.resizeNavigator();
	return;
    }

    $.getJSON('/template',
              {'template_id': 'navigator',
	       'session_id': this.gaweb.session_id},
	      function(data) {
		  // Create the dialog and buttons
		  var container;
		  container = $('<div>');
		  container.attr('id', 'navigator-dialog');
		  container.html(data.template);

		  container.dialog({title: 'Navigator',
			            close: function() {
			              container.empty();
				      container.remove();
			            },
			            width: width,
			            height: height,
			            position: [smartX, smartY]
			      });

		  $('button').button();
		  $('#select-spatial-pointbox-set').buttonset();

		  // Helper function: clears the openlayers map and makes a new one
		  //
		  // Returns:
		  //   an OpenLayers.Map instance
		  var remakeMap = function() {
		      $('#map').empty();

		      var map = new OpenLayers.Map({div: 'map', 
						    numZoomLevels: 1});
		      
		      // Add WMS Layer
		      var wmsLayer = new OpenLayers.Layer.WMS("OpenLayers WMS",
							      "http://vmap0.tiles.osgeo.org/wms/vmap0?",
		                                               {layers: 'basic'});
		      map.addLayers([wmsLayer]);
		      map.setCenter(new OpenLayers.LonLat(0, 0), 5); 

		      // Remove existing controls
		      var a = map.getControlsBy('id', /(.*)/);
		      for (var i=0; i < a.length; i++) {
			  map.removeControl(a[i]);
		      }
		      
		      return map;
		  }
		  
		  // Helper function: carries out an intent from this dialog to change the bounding box.
		  var carryOutChange = function(wlon, slat, elon, nlat) {
		      if (wlon === undefined
			  && elon === undefined 
			  && slat === undefined
			  && nlat === undefined) {
			  var message = $('<div>');
			  message.append('Please select a box on the provided map.');
			  message.dialog({title: 'Error', modal: true});				  
			  return;
		      }
		      
		      var command = '';			  
		      command += 'set lon ' + wlon + ' ' + elon + '\n';
		      command += 'set lat ' + slat + ' ' + nlat + '\n'; 
		      me.redoLastDisplayAfter(command);

		      $('#select-spatial').hide();
		      $('#select-spatial-link').show();
		  };

		  // Helper function: switches openlayers map to drawing a point
		  var switchToPoint = function(map) { 
		      var map = remakeMap();

		      // Add draw control, setup and activate
		      var drawControl = new OpenLayers.Control();		  
		  
		      OpenLayers.Util.extend(drawControl, {
			      draw: function() {
				  this.point = new OpenLayers.Handler.Point(drawControl, {"done": this.notice}, {});
				  this.point.activate();
			      },
			      notice: function(point) {
				  var a = point.getBounds().toBBOX().split(',');				  
				  carryOutChange(a[1], a[0], a[1], a[0]);
				  $('#navigator-dialog').dialog('option', 'width', width);
			      }

			  });
		      
		      map.addControl(drawControl);
		      drawControl.activate();
		  }

		  // Helper function: switches openlayers map to drawing a box
		  var switchToBox = function(map) {
		      var map = remakeMap();
		      
		      // Add draw control, setup and activate
		      var drawControl = new OpenLayers.Control();		  
		  
		      OpenLayers.Util.extend(drawControl, {
			      draw: function() {
				  this.box = new OpenLayers.Handler.Box(drawControl, {"done": this.notice}, {});
				  this.box.activate();
			      },
			      notice: function(bounds) {
				  var ll = map.getLonLatFromPixel(new OpenLayers.Pixel(bounds.left, bounds.bottom)); 
				  var ur = map.getLonLatFromPixel(new OpenLayers.Pixel(bounds.right, bounds.top)); 
				  carryOutChange(ll.lon.toFixed(4), ll.lat.toFixed(4),
						 ur.lon.toFixed(4), ur.lat.toFixed(4));
				  $('#navigator-dialog').dialog('option', 'width', width);
			      }
			  });
		      
		      map.addControl(drawControl);
		      drawControl.activate(); 		      
		  }

		  // -------------------------------------------------
		  // Create UI elements and set button callbacks
		  //
		  // The code to attach functionarlity to this dialog is seperated
		  // into two parts:
		  // - GaWebUI.refreshNavigatorDialog(data.dims, data.ctlinfo)
		  // - here.
		  //
		  // Generally, any code that depends on the dim state should be in the
		  // refresh method, and anything that doesn't (and should only happen
		  // once) should go here.
		  switchToBox();

		  $('#select-t-tabs').tabs();

		  $('#fixed-time-date-picker').datepicker({
			      changeMonth: true,
			      changeYear: true,
			      dateFormat: 'ddMyy',
			      onSelect: function(dateText, inst) {			        
			        $('#fixed-time-date-picker').trigger('onSelect');  // jquery bug workaround.
			  }});			  


		  $('#varying-time-date-picker1').datepicker({
			      changeMonth: true,
			      changeYear: true,
			      dateFormat: 'ddMyy',
			      onSelect: function(dateText, inst) {			        
			        $('#varying-time-date-picker1').trigger('onSelect');  // jquery bug workaround.
			  }});			  


		  $('#varying-time-date-picker2').datepicker({
			      changeMonth: true,
			      changeYear: true,
			      dateFormat: 'ddMyy',
			      onSelect: function(dateText, inst) {			        
			        $('#varying-time-date-picker2').trigger('onSelect');  // jquery bug workaround.
			  }});			  

		  me.refreshNavigatorDialog(data.dims, data.ctlinfo);
	       		 
		  // Select Z link
		  $('#select-z-link').click(function() {
			  $('#select-z').show('fast');
		      });

		  $('#select-z-close').click(function() {
			  $('#select-z-link').show();
			  $('#select-z').hide();
		      });

		  // Select T links
		  $('#select-t-link').click(function() {
			  $('#select-t').show('fast');
		      });

		  $('#select-t-close').click(function() {
			  $('#select-t-link').show();
			  $('#select-t').hide();
		      });
		  
		  // Select Spatial
		  $('#select-spatial-link').click(function() {			  
			  $('#select-spatial').show('fast');
			  $('#navigator-dialog').dialog('option', 'width', width + 60);
		      });
		  
		  $('#select-spatial-point').click(function() {	       			  
			  switchToPoint();
		      });

		  $('#select-spatial-box').click(function() {	       			  
			  switchToBox();
		      });

		  $('#select-spatial-whole-globe').click(function() {
			  $('#select-spatial').hide();
			  carryOutChange(-180, -90, 180, 90);
			  $('#navigator-dialog').dialog('option', 'width', width);
		      });

		  $('#select-spatial-close').click(function() {
			  $('#navigator-dialog').dialog('option', 'width', width);
			  $('#select-spatial').hide();
			  $('#select-spatial-link').show();
		      });
	      } // function(data)
	    ) // getJSON
};  // function
    

// Displays the "Open" dialog to the user
GaWebUI.prototype.showOpenDialog = function() {
    if ($('#open-dialog').length > 0) {
	$('#open-dialog').dialog('moveToTop');
	return;
    }

    var me = this;
    $.getJSON('/template',
	      {'template_id': 'open'},
	      function(data) {
		  var container;
		  container = $('<div>');
		  container.attr('id', 'open-dialog');
		  container.html(data.template);

		  var shortHeight = 200;
		  var tallHeight = 420;
		  
		  // Create the dialog and buttons
		  container.dialog({title: 'Open Dataset',
			            width: 470,
			            height: (data.featured == null) ? shortHeight : tallHeight,
				    close: function() {			      
					container.remove();
				    }});

		  $("button").button();
		  
		  // Attach callbacks to switchers
		  $('#featured-switcher').click(function() {
		      $('#open-dialog').dialog('option', 'height', tallHeight);
   		      $('#featured-content').fadeIn();
		      $('#gds-content').hide();
		      $('#sample-content').hide();
		   });

		  $('#gds-switcher').click(function() {
		      $('#open-dialog').dialog('option', 'height', shortHeight);
		      $('#featured-content').hide();
		      $('#gds-content').fadeIn();
		      $('#sample-content').hide();
		  });

		  $('#sample-switcher').click(function() {
		      $('#open-dialog').dialog('option', 'height', shortHeight);
		      $('#featured-content').hide();
		      $('#gds-content').hide();
		      $('#sample-content').fadeIn();
		  });
		  
		  // Set the default display
		  if (data.featured) {
		      $('#featured-switcher').click();
		  } else {
		      $('#gds-switcher').click();
		  }

		  // Accepts two classes of uri's: those that begin with http:// (case insens) and 
		  // those that start with a file path
		  var openUri = function(uri) {
		      var command;
		      if (uri.toLowerCase().indexOf('http://') == 0) {
			  command = 'sdfopen ' + uri;
		      } else if (uri.toLowerCase().match(/.ctl$/)) {
			  command = 'open ' + uri;
		      } else {
			  command = 'open ' + uri; // might as well try
		      }
			      
		      me.term.echo('ga-> ' + command);
		      me.gaweb.handleTerminal(command, me.gaweb.term);
		      container.dialog('close');
		      me.showDisplayDialog();
		      me.showNavigatorDialog();
		  }

		  // Attach callbacks to featured open links
		  if (data.featured !== null) {
		      var getSingleValue = function(o) {
			  var v = null;
			  for (var key in o) {
			      v = o[key];
			  }
			  return v;
		      }
		      
		      var featured = data.featured.featured;
		      for (var i=0; i < featured.length; i++) {
			  var v = getSingleValue(featured[i]);
			  
			  for (var j=0; j < v.length; j++) {	
			      var url = getSingleValue(v[j]);
			      $('#sec-'+i+'-'+j+'-button').click((function(datasetUrl) {
				      return function() {
					  openUri(datasetUrl);
				      }
				      })(url));
			  }
		      }
		  }
	       
		  $('#open-gds-submit').click(function() {
		      var url = $('#open-gds-dataset-url').val();

		      if (url.indexOf('http://') != 0) {
			  alert('That GDS url is not valid.');
		      } else {
			  openUri(url);
		      }
		  });

		  $('#open-monsoon-link').click(function() {
			  openUri('http://monsoondata.org:9090/dods/model');
		      });
		  
		  $('#open-grads-link').click(function() {
			  openUri('/opt/opengrads/Resources/SampleDatasets/model.ctl');
		      });

	      });
}

// Launcher to show the terminal from the menu. Opens a terminal dialog, expecting 
// the terminal itself to already exist.
GaWebUI.prototype.showTerminalFromMenu = function() {
    var smartX = $(window).width() * .55;
    var smartY = $(window).height() * .25;
    
    $('#grads-terminal').dialog({title: 'GrADS Terminal',
		height: 200,
		width: 400,
		position: [smartX, smartY]
	});
    
    $('#grads-terminal').css('background-color', 'black');
}

    
